Here is a highly portable snprintf that we use on a bunch of embedded controller software; it has been 'configure'd to work with GNU autoconf. Your milage may vary. Enjoy. Prof. Patrick Powell Dept. Electrical and Computer Engineering, San Diego State University, San Diego, CA 92182-1309 Office (619) 594-7796; Lab (619) 594-7578 FAX (619) 594-7577 email: papowell@sdsu.edu ---- Cut Here and feed the following to sh ---- #!/bin/sh # This is a shell archive (produced by GNU sharutils 4.1). # To extract the files from this archive, save it to some FILE, remove # everything before the `!/bin/sh' line above, then type `sh FILE'. # # Made on 1995-08-30 09:38 PDT by <papowell@dickory>. # Source directory was `/tmp'. # # Existing files will *not* be overwritten unless `-c' is specified. # # This shar contains: # length mode name # ------ ---------- ------------------------------------------ # 522 -rw-r--r-- README.snprintf # 6630 -rw-r--r-- snprintf.c # 1100 -rw-r--r-- snprintf.h # touch -am 1231235999 $$.touch >/dev/null 2>&1 if test ! -f 1231235999 && test -f $$.touch; then shar_touch=touch else shar_touch=: echo echo 'WARNING: not restoring timestamps. Consider getting and' echo "installing GNU \`touch', distributed in GNU File Utilities..." echo fi rm -f 1231235999 $$.touch # # ============= README.snprintf ============== if test -f 'README.snprintf' && test X"$1" != X"-c"; then echo 'x - skipping README.snprintf (file already exists)' else echo 'x - extracting README.snprintf (text)' sed 's/^X//' << 'SHAR_EOF' > 'README.snprintf' && Tue Aug 29 17:30:22 PDT 1995 Patrick Powell This is a very idiot level version of SNPRINTF that can be ported to several different systems. Note that the lobotomized dopr_outch() routine at the end of the snprintf.c file makes sure that no funny control characters get printed out. X If you have CONFIGURE, add the following lines to the configure.in script- these detect the prescence of the varargs.h or stdarg.h files: X AC_CHECK_HEADERS(varargs.h stdarg.h) X The snprintf code in the attached files is fairly portable. X SHAR_EOF $shar_touch -am 0829174195 'README.snprintf' && chmod 0644 'README.snprintf' || echo 'restore of README.snprintf failed' shar_count="`wc -c < 'README.snprintf'`" test 522 -eq "$shar_count" || echo "README.snprintf: original size 522, current size $shar_count" fi # ============= snprintf.c ============== if test -f 'snprintf.c' && test X"$1" != X"-c"; then echo 'x - skipping snprintf.c (file already exists)' else echo 'x - extracting snprintf.c (text)' sed 's/^X//' << 'SHAR_EOF' > 'snprintf.c' && /************************************************************** X * Original: X * Patrick Powell Tue Apr 11 09:48:21 PDT 1995 X * A bombproof version of doprnt (dopr) included. X * Sigh. This sort of thing is always nasty do deal with. Note that X * the version here does not include floating point... X * X * plp_snprintf() is used instead of sprintf() as it does limit checks X * for string length. This covers a nasty loophole. X * X * The other functions are there to prevent NULL pointers from X * causing nast effects. X **************************************************************/ X static char _id[] = "$Id: snprintf.c,v 1.1 1995/08/19 20:36:09 papowell Exp $"; static void dopr(); static char *end; X #if defined(HAVE_CONFIG_H) # include config.h #endif X /* varargs declarations: */ X #if defined(HAVE_STDARG_H) # include <stdarg.h> # define HAVE_STDARGS /* let's hope that works everywhere (mj) */ # define VA_LOCAL_DECL va_list ap; # define VA_START(f) va_start(ap, f) # define VA_SHIFT(v,t) ; /* no-op for ANSI */ # define VA_END va_end(ap) #else # if defined(HAVE_VARARGS_H) # include <varargs.h> # undef HAVE_STDARGS # define VA_LOCAL_DECL va_list ap; # define VA_START(f) va_start(ap) /* f is ignored! */ # define VA_SHIFT(v,t) v = va_arg(ap,t) # define VA_END va_end(ap) # else XXX ** NO VARARGS ** XX # endif #endif X #ifdef HAVE_STDARGS int plp_snprintf (char *str, size_t count, const char *fmt, ...); int vplp_snprintf (char *str, size_t count, const char *fmt, va_list arg); void setproctitle( char *fmt, ... ); #else int plp_snprintf (); int vplp_snprintf (); void setproctitle(); #endif X int vplp_snprintf(str, count, fmt, args) X char *str; X size_t count; X const char *fmt; X va_list args; { X str[0] = 0; X end = str+count-1; X dopr( str, fmt, args ); X if( count>0 ){ X end[0] = 0; X } X return(strlen(str)); } X /* VARARGS3 */ #ifdef HAVE_STDARGS int plp_snprintf (char *str,size_t count,const char *fmt,...) #else int plp_snprintf (va_alist) va_dcl #endif { #ifndef HAVE_STDARGS X char *str; X size_t count; X char *fmt; #endif X VA_LOCAL_DECL X X VA_START (fmt); X VA_SHIFT (str, char *); X VA_SHIFT (count, size_t ); X VA_SHIFT (fmt, char *); X (void) vplp_snprintf ( str, count, fmt, ap); X VA_END; X return( strlen( str ) ); } X /* X * dopr(): poor man's version of doprintf X */ X static void fmtstr( char *value, int ljust, int len, int zpad ); static void fmtnum( long value, int base, int dosign, X int ljust, int len, int zpad ); static void dostr( char * ); static char *output; static void dopr_outch( int c ); X static void dopr( buffer, format, args ) X char *buffer; X char *format; X va_list args; { X int ch; X long value; X int longflag = 0; X char *strvalue; X int ljust; X int len; X int zpad; X X output = buffer; X while( (ch = *format++) ){ X switch( ch ){ X case '%': X ljust = len = zpad = 0; X nextch: X ch = *format++; X switch( ch ){ X case 0: X dostr( "**end of format**" ); X return; X case '-': ljust = 1; goto nextch; X case '0': /* set zero padding if len not set */ X if(len==0) zpad = '0'; X case '1': case '2': case '3': X case '4': case '5': case '6': X case '7': case '8': case '9': X len = len*10 + ch - '0'; X goto nextch; X case 'l': longflag = 1; goto nextch; X case 'u': case 'U': X /*fmtnum(value,base,dosign,ljust,len,zpad) */ X if( longflag ){ X value = va_arg( args, long ); X } else { X value = va_arg( args, int ); X } X fmtnum( value, 10,0, ljust, len, zpad ); break; X case 'o': case 'O': X /*fmtnum(value,base,dosign,ljust,len,zpad) */ X if( longflag ){ X value = va_arg( args, long ); X } else { X value = va_arg( args, int ); X } X fmtnum( value, 8,0, ljust, len, zpad ); break; X case 'd': case 'D': X if( longflag ){ X value = va_arg( args, long ); X } else { X value = va_arg( args, int ); X } X fmtnum( value, 10,1, ljust, len, zpad ); break; X case 'x': X if( longflag ){ X value = va_arg( args, long ); X } else { X value = va_arg( args, int ); X } X fmtnum( value, 16,0, ljust, len, zpad ); break; X case 'X': X if( longflag ){ X value = va_arg( args, long ); X } else { X value = va_arg( args, int ); X } X fmtnum( value,-16,0, ljust, len, zpad ); break; X case 's': X strvalue = va_arg( args, char *); X fmtstr( strvalue,ljust,len,zpad ); break; X case 'c': X ch = va_arg( args, int ); X dopr_outch( ch ); break; X case '%': dopr_outch( ch ); continue; X default: X dostr( "???????" ); X } X longflag = 0; X break; X default: X dopr_outch( ch ); X break; X } X } X *output = 0; } X static void fmtstr( value, ljust, len, zpad ) X char *value; X int ljust, len, zpad; { X int padlen, strlen; /* amount to pad */ X X if( value == 0 ){ X value = "<NULL>"; X } X for( strlen = 0; value[strlen]; ++ strlen ); /* strlen */ X padlen = len - strlen; X if( padlen < 0 ) padlen = 0; X if( ljust ) padlen = -padlen; X while( padlen > 0 ) { X dopr_outch( ' ' ); X --padlen; X } X dostr( value ); X while( padlen < 0 ) { X dopr_outch( ' ' ); X ++padlen; X } } X static void fmtnum( value, base, dosign, ljust, len, zpad ) X long value; X int base, dosign, ljust, len, zpad; { X int signvalue = 0; X unsigned long uvalue; X char convert[20]; X int place = 0; X int padlen = 0; /* amount to pad */ X int caps = 0; X X /* DEBUGP(("value 0x%x, base %d, dosign %d, ljust %d, len %d, zpad %d\n", X value, base, dosign, ljust, len, zpad )); */ X uvalue = value; X if( dosign ){ X if( value < 0 ) { X signvalue = '-'; X uvalue = -value; X } X } X if( base < 0 ){ X caps = 1; X base = -base; X } X do{ X convert[place++] = X (caps? "0123456789ABCDEF":"0123456789abcdef") X [uvalue % (unsigned)base ]; X uvalue = (uvalue / (unsigned)base ); X }while(uvalue); X convert[place] = 0; X padlen = len - place; X if( padlen < 0 ) padlen = 0; X if( ljust ) padlen = -padlen; X /* DEBUGP(( "str '%s', place %d, sign %c, padlen %d\n", X convert,place,signvalue,padlen)); */ X if( zpad && padlen > 0 ){ X if( signvalue ){ X dopr_outch( signvalue ); X --padlen; X signvalue = 0; X } X while( padlen > 0 ){ X dopr_outch( zpad ); X --padlen; X } X } X while( padlen > 0 ) { X dopr_outch( ' ' ); X --padlen; X } X if( signvalue ) dopr_outch( signvalue ); X while( place > 0 ) dopr_outch( convert[--place] ); X while( padlen < 0 ){ X dopr_outch( ' ' ); X ++padlen; X } } X static void dostr( str ) X char *str; { X while(*str) dopr_outch(*str++); } X static void dopr_outch( c ) X int c; { X if( iscntrl(c) && c != '\n' && c != '\t' ){ X c = '@' + (c & 0x1F); X if( end == 0 || output < end ){ X *output++ = '^'; X } X } X if( end == 0 || output < end ){ X *output++ = c; X } } SHAR_EOF $shar_touch -am 0829173595 'snprintf.c' && chmod 0644 'snprintf.c' || echo 'restore of snprintf.c failed' shar_count="`wc -c < 'snprintf.c'`" test 6630 -eq "$shar_count" || echo "snprintf.c: original size 6630, current size $shar_count" fi # ============= snprintf.h ============== if test -f 'snprintf.h' && test X"$1" != X"-c"; then echo 'x - skipping snprintf.h (file already exists)' else echo 'x - extracting snprintf.h (text)' sed 's/^X//' << 'SHAR_EOF' > 'snprintf.h' && /* if you have configure you can use this */ #if defined(HAVE_CONFIG_H) # include config.h #endif X /* varargs declarations: */ /* you might have to hand force this by doing #define HAVE_STDARG_H */ X #if defined(HAVE_STDARG_H) # include <stdarg.h> # define HAVE_STDARGS /* let's hope that works everywhere (mj) */ # define VA_LOCAL_DECL va_list ap; # define VA_START(f) va_start(ap, f) # define VA_SHIFT(v,t) ; /* no-op for ANSI */ # define VA_END va_end(ap) #else # if defined(HAVE_VARARGS_H) # include <varargs.h> # undef HAVE_STDARGS # define VA_LOCAL_DECL va_list ap; # define VA_START(f) va_start(ap) /* f is ignored! */ # define VA_SHIFT(v,t) v = va_arg(ap,t) # define VA_END va_end(ap) # else XXX ** NO VARARGS ** XX # endif #endif X /* you can have ANSI C definitions */ #ifdef HAVE_STDARGS int plp_snprintf (char *str, size_t count, const char *fmt, ...); int vplp_snprintf (char *str, size_t count, const char *fmt, va_list arg); void setproctitle( char *fmt, ... ); #else int plp_snprintf (); int vplp_snprintf (); void setproctitle(); #endif X X SHAR_EOF $shar_touch -am 0829174095 'snprintf.h' && chmod 0644 'snprintf.h' || echo 'restore of snprintf.h failed' shar_count="`wc -c < 'snprintf.h'`" test 1100 -eq "$shar_count" || echo "snprintf.h: original size 1100, current size $shar_count" fi exit 0